#include <algorithm>
#include <list>
#include "Emulator.h"
#include "CommandCreator.h"
#include "VariableDict.h"
#include "Functions.h"

using namespace std;

void Emulator::executeBlockCandidate() {
  if (blockProgramsIds.size() == 0) {
    return;
  }        
  int progId = blockProgramsIds[0];  
  blockProgramsIds.erase(blockProgramsIds.begin());
  for (unsigned int i = 0; i < programs.size(); i++ ) {
    if (programs[i].getId() == progId) {
      programs[i].makeTimeQuant(blockProgramsIds, lockId, variables.get(), outStream);    
      break;
    }
  }    
}

void Emulator::executeProgramVector() {        
  resetProgramsState();
    
  for (unsigned int i = 0; i < programs.size(); i++) {
    programs[i].makeTimeQuant(blockProgramsIds, lockId, variables.get(), outStream);      
    if (lockId == 0) {//if executed unlock
      executeBlockCandidate();//call program, that asked for lock
    }     
  }
  deleteFinishedPrograms();
}

bool isFinishedProgram(Program &p) {
  return p.isFinished();
}
//remove all executed programs
void Emulator::deleteFinishedPrograms() {    
  std::vector<Program>::iterator newEnd = remove_if(programs.begin(), programs.end(), isFinishedProgram);
  programs.erase(newEnd, programs.end()); 
}

void Emulator::resetProgramsState() {
  for (unsigned int i = 0; i < programs.size(); i++) {
    programs[i].resetProgram();
  }    
}
/*
bool isEndCommand(const std::string &command) {
  //static EndParser parser;
  return EndParser::isEndCommand(command);
}
  */
Emulator::Emulator(const std::list<std::string> &lines, std::ostream &outStream_) : 
                    variables(new VariableDict()), infoAnalizer(InfoAnalizer(lines.front())), 
                    outStream(outStream_), lockId(0)  {  

  list<string>::const_iterator begin = lines.cbegin();    
  //first line contains time characteristics, not command code
  begin++;

  int id = 1;
  while (begin != lines.cend()) {
    list<string>::const_iterator progEnd = find_if(begin, lines.cend(), &EndParser::isEndCommand);
    if (progEnd != lines.cend()) {
      progEnd++;
    }
    programs.push_back(Program(id, begin, progEnd));
    begin = progEnd;
    id++;
  }
  if (programs.size() != infoAnalizer.getNumberOfPrograms()) {
    throw std::logic_error("error in input file - wrong number of programs");
  }
}  
  
void Emulator::execute() {    
  while (programs.size() != 0) {
    executeProgramVector();
  }
}

Emulator::~Emulator() {          
  programs.clear();
}
